import 'dart:convert';
import 'dart:io';

import 'package:get/get.dart';
import 'package:largefriends/events/recall_message_event.dart';
import 'package:largefriends/net/lf_image_upload.dart';
import 'package:largefriends/service/im/lf_im_server_msg_ext.dart';
import 'package:web_socket_channel/web_socket_channel.dart';
import 'package:wechat_assets_picker/wechat_assets_picker.dart';

import '../../config/lf_im_type.dart';
import '../../entity/entities/chat_new_message_entity.dart';
import '../manager/lf_event_helper.dart';
import '../../net/lf_api_util.dart';
import '../../res/lf_int_ext.dart';
import '../../utils/lf_logger.dart';
import 'lf_im_service.dart';

extension IMServiceSendExt on LfIMService {
  Future<bool> sendTextMessage(
      {required String message,
      required String receiverId,
      required Function() insertLocal}) async {
    var localId = _addLocalMessage(
        text: message,
        messageType: parseInt(IMMessageType.text.value),
        otherUid: receiverId);
    if (localId.isEmpty) {
      return Future(() => false);
    }
    insertLocal.call();
    var result = await _sendMessageToService(
        _createTextMessageData(message, receiverId, localId));
    return result;
  }

  Future<bool> sendReplyMessage(
      {required String message,
      required String receiverId,
      required ReplyInfo replyInfo,
      required Function() insertLocal}) async {
    var localId = _addLocalMessage(
        text: message,
        replyInfo: replyInfo,
        messageType: parseInt(IMMessageType.reply.value),
        otherUid: receiverId);

    if (localId.isEmpty) {
      return Future(() => false);
    }
    insertLocal.call();

    var result = await _sendMessageToService(
        _createReplyMessageData(message, receiverId, localId, replyInfo));
    return result;
  }

  Future<bool> sendImage(
      {required AssetEntity entity,
      required String receiverId,
      required double width,
      required double height,
      required Function() insertLocal}) async {
    var file = await entity.file;
    var localImagePath = file?.path;
    var localId = _addLocalMessage(
        text: '[IMAGE]',
        messageType: parseInt(IMMessageType.image.value),
        url: '',
        localImagePath: localImagePath,
        width: width,
        height: height,
        otherUid: receiverId);
    if (localId.isEmpty) {
      return Future(() => false);
    }
    insertLocal.call();
    var list = await uploadFile(needCompress: true, fileList: [entity]);
    var attachId = list.isNotEmpty ? list.first.attachId ?? "" : "";
    var imageUrl = list.isNotEmpty ? list.first.url ?? "" : "";
    if (attachId.isEmpty || imageUrl.isEmpty) {
      return Future(() => false);
    }
    var result = await _sendMessageToService(
        _createImageData(attachId, imageUrl, receiverId, localId));
    return result;
  }

  Future<bool> sendAudio(
      {required File file,
      required String receiverId,
      required Function() insertLocal,
      required int duration}) async {
    var localId = _addLocalMessage(
        text: '[Audio]',
        messageType: parseInt(IMMessageType.voice.value),
        url: file.path,
        duration: duration,
        otherUid: receiverId);
    if (localId.isEmpty) {
      return Future(() => false);
    }
    insertLocal.call();
    var audio = await uploadAudio(file, duration);

    if (audio == null) {
      return Future(() => false);
    }
    var attachId = audio.attachId ?? "";
    var audioUrl = audio.url ?? "";

    if (attachId.isEmpty || audioUrl.isEmpty) {
      return Future(() => false);
    }

    var result = await _sendMessageToService(
        _createAudioData(attachId, audioUrl, receiverId, localId, duration));
    return result;
  }

  Future<bool> sendSticker(
      {required String message,
      required String receiverId,
      required IMMessageType imMessageType,
      required Function() insertLocal}) async {
    var localId = _addLocalMessage(
        text: message.trim(),
        messageType: parseInt(imMessageType.value),
        otherUid: receiverId);
    if (localId.isEmpty) {
      return Future(() => false);
    }
    insertLocal.call();

    var result = await _sendMessageToService(_createStickerMessageData(
        localId: localId,
        imMessageType: imMessageType,
        receiverId: receiverId,
        message: message));
    return result;
  }

  Future<bool> sendRecall(
      {required String receiverId,
      required ChatNewMessageEntity entity,
      required Function() insertLocal}) async {
    int index = getMessageListFromMemory(receiverId).indexOf(entity);
    if (index == 0) {
      ///只有第一个的时候,才会进行处理
      LfEventHelper().getEvent().fire(RecallMessageEvent(receiverId));
    }
    var localItem = getMessageListFromMemory(receiverId)
        .firstWhereOrNull((element) => element.historyId == entity.historyId);
    if (localItem == null) {
      return Future(() => false);
    }
    localItem.messageType = IMMessageType.messageTypeRecall.value;
    localItem.message = 'You\'ve unsent a message.';
    insertLocal.call();
    var messageData = _createRecallMessage(receiverId, entity);
    var result = await _sendMessageToService(messageData);
    return result;
  }

  void reportReadTime(String? fromUid) async {
    Map<String, dynamic> messageData = {};
    messageData.putIfAbsent('type', () => "report");
    messageData["to"] = fromUid;
    _sendMessageToService(messageData);
  }

  Future<bool> _sendMessageToService(Map messageData) async {
    if (connectionStatus.value != ConnectionStatusEnum.connected ||
        channel?.closeCode != null) {
      await connect();
    }
    try {
      channel?.sink.add(json.encode(messageData).toString());
      lfLogger.w('IMService----${channel?.closeCode}-${channel?.closeReason}}');
      return Future(() => true);
    } on WebSocketChannelException catch (e) {
      lfLogger.w('IMService-----$e');
      return Future(() => false);
    }
  }

  _createTextMessageData(String message, String receiverId, String localId) {
    Map<String, dynamic> messageData = {};
    messageData.putIfAbsent("type", () => MessageTypeEnum.say.value);
    messageData.putIfAbsent("to", () => receiverId);
    messageData.putIfAbsent("message", () => message);
    messageData.putIfAbsent("type_id", () => IMMessageType.text.value);
    messageData.putIfAbsent("local_id", () => localId);
    return messageData;
  }

  _createReplyMessageData(
      String message, String receiverId, String localId, ReplyInfo replyInfo) {
    Map<String, dynamic> messageData = {};
    messageData.putIfAbsent("type", () => MessageTypeEnum.say.value);
    messageData.putIfAbsent("to", () => receiverId);
    messageData.putIfAbsent("message", () => message);
    messageData.putIfAbsent("type_id", () => IMMessageType.reply.value);
    messageData.putIfAbsent("local_id", () => localId);
    messageData.putIfAbsent("reply_to", () => replyInfo.historyId);
    messageData.putIfAbsent("reply_content", () => replyInfo.content);
    messageData.putIfAbsent("reply_to_uname", () => replyInfo.toUsername);
    return messageData;
  }

  buildMarkReadData({required String messageId, required String receiverId}) {
    Map<String, dynamic> messageData = {};
    messageData.putIfAbsent("type", () => MessageTypeEnum.report.value);
    messageData.putIfAbsent("to", () => receiverId);
    messageData.putIfAbsent("message_id", () => messageId);
    return messageData;
  }

  _createImageData(
      String attachId, String imageUrl, String receiverId, String localId) {
    Map<String, dynamic> messageData = {};
    messageData.putIfAbsent("type", () => MessageTypeEnum.say.value);
    messageData.putIfAbsent("to", () => receiverId);
    messageData.putIfAbsent("type_id", () => IMMessageType.image.value);
    messageData.putIfAbsent("attach_id", () => attachId);
    messageData.putIfAbsent("url", () => imageUrl);
    messageData.putIfAbsent("local_id", () => localId);
    messageData.putIfAbsent("message", () => '[image]');
    return messageData;
  }

  _createAudioData(String attachId, String audioUrl, String receiverId,
      String localId, int duration) {
    Map<String, dynamic> messageData = {};
    messageData.putIfAbsent("type", () => MessageTypeEnum.say.value);
    messageData.putIfAbsent("to", () => receiverId);
    messageData.putIfAbsent("type_id", () => IMMessageType.voice.value);
    messageData.putIfAbsent("attach_id", () => attachId);
    messageData.putIfAbsent("url", () => audioUrl);
    messageData.putIfAbsent("duration", () => duration);
    messageData.putIfAbsent("local_id", () => localId);
    messageData.putIfAbsent("message", () => '[audio]');
    return messageData;
  }

  _createStickerMessageData(
      {required String message,
      required String receiverId,
      required String localId,
      required IMMessageType imMessageType}) {
    Map<String, dynamic> messageData = {};
    messageData.putIfAbsent("type", () => MessageTypeEnum.say.value);
    messageData.putIfAbsent("to", () => receiverId);
    messageData.putIfAbsent("message", () => message);
    messageData.putIfAbsent("type_id", () => imMessageType.value);
    messageData.putIfAbsent("local_id", () => localId);
    return messageData;
  }

  _createRecallMessage(
    String otherUid,
    ChatNewMessageEntity entity,
  ) {
    var self = authService.currentAccount;
    if (self == null) {
      return '';
    }
    Map<String, dynamic> messageData = {};
    messageData.putIfAbsent("type", () => MessageTypeEnum.recall.value);
    messageData.putIfAbsent("to_uid", () => otherUid);
    messageData.putIfAbsent('message_id', () => entity.historyId);
    messageData.putIfAbsent("message", () => 'You\'ve unsent a message.');
    messageData.putIfAbsent(
        "type_id", () => IMMessageType.messageTypeRecall.value);
    messageData.putIfAbsent("local_id", () => createUUID());
    messageData.putIfAbsent('time', () => entity.created);
    messageData.putIfAbsent('room_id', () => entity.roomId);
    messageData.putIfAbsent('show_recall', () => 1);
    messageData.putIfAbsent('from_name', () => self.username);
    messageData.putIfAbsent('from_uid', () => self.userId);
    return messageData;
  }

  /// 返回localId
  String _addLocalMessage({
    required String text,
    required int messageType,
    required String otherUid,
    String? url,
    String? localImagePath,
    double? width,
    double? height,
    ReplyInfo? replyInfo,
    int? duration,
  }) {
    ///本地添加消息体
    ChatNewMessageEntity chatNewMessageEntity = ChatNewMessageEntity();
    chatNewMessageEntity.created = currentTimeMillis();
    chatNewMessageEntity.message = text;
    chatNewMessageEntity.url = url;
    chatNewMessageEntity.localImagePath = localImagePath;
    chatNewMessageEntity.width = width;
    chatNewMessageEntity.height = height;
    chatNewMessageEntity.messageType = messageType.toString();
    chatNewMessageEntity.sendState = MessageStatusEnum.sending.value;
    chatNewMessageEntity.replyInfo = replyInfo;
    chatNewMessageEntity.duration = '$duration';
    Sender sender = Sender();

    var self = authService.currentAccount;
    if (self == null) {
      return '';
    }
    var localId = createUUID();
    Profile profile = Profile()
      ..about = self.about
      ..age = self.age
      ..gender = self.gender
      ..member = self.member
      ..online = self.online
      ..username = self.username
      ..userId = self.userId
      ..status = self.status
      ..verified = self.verified
      ..avatarUrl = self.avatar;

    sender.profile = profile;
    chatNewMessageEntity.sender = sender;
    chatNewMessageEntity.localId = localId;
    getMessageListFromMemory(otherUid).insert(0, chatNewMessageEntity);
    return localId;
  }

  static int currentTimeMillis() {
    return DateTime.now().millisecondsSinceEpoch ~/
        Duration.microsecondsPerMillisecond;
  }
}
